#include "pipe.h"
#include "clog.h"

u32 pip_p_initialized = 0;

Pipe::Pipe()
{
	if ( !pip_p_initialized ) {
		pInit();
	}

	p_mx_brecv = PTHREAD_MUTEX_INITIALIZER;
	_only_read = 0;
	p_exit_flag = 0;
//    fl_connected = 0;
//    fl_connecting = 0;
//    fl_error = 0;
//    fl_idle = 1;
//    fl_readyread = 0;
}

Pipe::~Pipe()
{
	//dtor
}

int Pipe::Close ( i32 produce_error )
{
	p_exit_flag = 1;
	int res = closesocket ( p_socket );
	p_socket = -1;

	if ( ( res == -1 ) && ( WSAGetLastError() == WSAENOTSOCK ) && ( produce_error == 0 ) )
	{
//		LOGP1 ( "PIPE: Warning : closing on nonsocket." );
		return 0;
	}

	if ( res == -1 )
	{
		p_error ( ER_CLOSING );
		return 1;
	}

	return 0;
}

int Pipe::pInit()
{
	WSADATA wsaData;

	if ( WSAStartup ( MAKEWORD ( 1, 1 ), &wsaData ) != 0 )
	{
		EXCEX ( 0xFF01, "Error at init of Winsock" );
	}

	pip_p_initialized = 1;
	LOGP1 ( "Winsock initialized" );
	return 1;
}

int Pipe::Cleanup()
{
	if ( !pip_p_initialized )
	{
		EXCEX ( 0xFF02, "Can't cleanup when wsock not initialized!" );
	}

	WSACleanup();
	LOGP1 ( "Winsock closed" );
	pip_p_initialized = 0;
	return 0;
}

int Pipe::Connect ( string url, u16 port )
{
//    MX_FLAG_E;
//    if (fl_connected)
//        EXCEX(0xF001,"Already connected.");
//    if (fl_connecting)
//        EXCEX(0xF002,"Already connecting.");
//
//    fl_connecting = 1;
//    fl_idle = 0;
//    fl_readyread = 0;
//    fl_error = 0;
//    MX_FLAG_L;
	p_url = url;
	p_port = port;

	if ( !url.size() && !port )
	{
		LOGP1 ( "Starting thread for incoming connection..." );
	}
	else
	{
		LOGP3 ( "Connecting to [%s:%d]", url.c_str(), port );
	}

//    pthread_create( &p_thr_connect,0,Pipe::th_connect,(void*)this);
	pthread_create ( &p_thr_connect, 0, Pipe::th_connect, ( void * ) this );
	return 0;
}

void *Pipe::th_connect ( void *ptr )
{
	LOGP1 ( "[---]Thread begins here." );
	Pipe *pipe = ( Pipe * ) ptr;

	if ( !pipe->_only_read )
	{
		string url = pipe->p_url;
		u16 port = pipe->p_port;

		if ( ( pipe->p_srv_info = gethostbyname ( url.c_str() ) ) == NULL )
		{
			LOGP2 ( "URL resolution failed [%s]", url.c_str() );
			pipe->p_error ( ER_RESOLUTION );
			return 0;
		}

		LOGP2 ( "Connecting to %s...\n", inet_ntoa ( * ( ( in_addr * ) pipe->p_srv_info->h_addr ) ) );
		pipe->p_srv_addr.sin_family = AF_INET;    // host byte order
		pipe->p_srv_addr.sin_port = htons ( port ); // short, network byte order
		pipe->p_srv_addr.sin_addr = * ( ( in_addr * ) pipe->p_srv_info->h_addr );
		memset ( & ( pipe->p_srv_addr.sin_zero ), '\0', 8 );

		//printf("Addr %s, IP %s \n",he->h_name,inet_ntoa(*((struct in_addr *)he->h_addr)));
		if ( ( pipe->p_socket = socket ( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
		{
			LOGP1 ( "Can't Create socket : Error # 7" );
			pipe->p_error ( ER_SOCKET_CREATION );
			return 0;
		}

		if ( connect ( pipe->p_socket, ( struct sockaddr * ) &pipe->p_srv_addr,
					   sizeof ( struct sockaddr ) ) == -1 )
		{
			LOGP2 ( "Cant Connect to server : Error #1 [%d]", errno );
			pipe->p_error ( ER_CONNECTING );
			return 0;
		}
	}

	if ( !pipe->onConnected.empty() ) {
		pipe->onConnected ( pipe, 0, pipe->user_data );
	}

/// ------------------------------ conection phase ended. Now time to sync. read.
	static char rbuff[RB_SIZE];
	unsigned long lblock = 1;
//    if(ioctlsocket(pipe->p_socket,FIONBIO,&lblock)==SOCKET_ERROR)
//    {
//        LOGP1("Cant set block mode. Error #13");
//        pipe->p_error(ER_SETTING);
//        return 0;
//    }
	//======================================================

	while ( 1 )
	{
		if ( pipe->p_socket == -1 )
		{
			LOGP1 ( "Socket allready closed. Thread exiting..." );
			return 0;
		}

		i32 recived = recv ( pipe->p_socket, rbuff, RB_SIZE, 0 );

		if ( pipe->p_socket == -1 )
		{
			LOGP1 ( "Socket closed by this side. Thread exiting..." );
			pipe->p_error ( ST_CLOSED_BY_ME );
			return 0;
		}

		if ( recived == 0 )
		{
			LOGP1 ( "Connection closed by other side." );
			pipe->p_error ( ST_CLOSED );
			return 0;
		}

		if  ( WSAGetLastError() == 10054 )
		{
			LOGP1 ( "Connection closed by other side." );
			pipe->p_error ( ST_CLOSED );
			return 0;
		}

		if ( WSAGetLastError() == 10035 )
		{
			Sleep ( 1 );
			LOGP1 ( "PPP" );
			continue;
		}

//        LOGP4("RECIVED %d, syscode: %d, WSA %d",recived,errno,WSAGetLastError());
		ENTER_CS ( pipe->p_mx_brecv );

		for ( int c = 0; c < recived; c++ )
		{
			pipe->p_buff_recv.push_back ( rbuff[c] );
		}

		LEAVE_CS ( pipe->p_mx_brecv );

		if ( !pipe->onReadyRead.empty() )
		{
			pipe->onReadyRead ( pipe, recived, pipe->user_data );
		}
		else
		{
			LOGP2 ( "[WARNING] Data ready to read, but no reciver callback set. [%d]bytes", recived );
		}
	}

	return 0;
}

void Pipe::p_error ( int err_code, string err )
{
	if ( !onError.empty() )
	{
		onError ( this, err_code, user_data );
		return;
	}

	LOGP2 ( "[FATAL] Unhandled error in pipe [%X]", err_code );
	EXCEX ( err_code, err );
}

int Pipe::Send ( i8 *data, u32 len )
{
	int sent = 0;

	if ( ( sent = send ( p_socket, ( char * ) data, len, 0 ) ) == -1 )
	{
		LOGP1 ( "Cant Send data : Error #2" );
		p_error ( ER_SENDING );
		return 0;
	}

	//LOGP2("Sent [%d] bytes...",sent);
	return 1;
}

int Pipe::Recv ( i8 **data, u32 &len )
{
//    ENTER_CS (p_mx_brecv);
//
//    len = p_buff_recv.size();
//    *data = &p_buff_recv[0];
//
//    LEAVE_CS (p_mx_brecv);
	exit ( 0 );
	return 0;
}

int Pipe::ClearRecvBuff ( u32 len )
{
	ENTER_CS ( p_mx_brecv );

	if ( len >= p_buff_recv.size() )
	{
		p_buff_recv.clear();
		p_buff_recv.empty();
		LEAVE_CS ( p_mx_brecv );
		return 0;
	}

	p_buff_recv.erase ( p_buff_recv.begin(), p_buff_recv.begin() + len );
	LEAVE_CS ( p_mx_brecv );
	return 0;
}


string ErrToStr ( i32 sys_err )
{
	char lpszFunction[256];
	LPVOID lpMsgBuf;
	LPVOID lpDisplayBuf;
	FormatMessage (
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		sys_err,
		MAKELANGID ( LANG_NEUTRAL, SUBLANG_DEFAULT ),
		( LPTSTR ) &lpMsgBuf,
		0, NULL );
	// Display the error message and exit the process
	lpDisplayBuf = ( LPVOID ) LocalAlloc ( LMEM_ZEROINIT,
										   ( lstrlen ( ( LPCTSTR ) lpMsgBuf ) + lstrlen ( ( LPCTSTR ) lpszFunction ) + 40 ) * sizeof ( TCHAR ) );
	string ret;
	char buff[16];
	snprintf ( buff, 16, "%d --> ", sys_err );
	ret = buff;
	ret += ( char * ) lpMsgBuf;

	if ( ret[ret.size() - 1] == '\n' ) {
		ret[ret.size() - 1] = 0;
	}

	LocalFree ( lpMsgBuf );
	LocalFree ( lpDisplayBuf );
	return ret;
}



